﻿<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>GoJS Changed Events -- Northwoods Software</title>
  <!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
  <script src="../release/go.js"></script>
  <script src="goIntro.js"></script>
</head>
<body onload="goIntro()">
<div id="container" class="container-fluid">
<div id="content">

<h1>Changed Events</h1>
<p>
There are three basic kinds of events that <b>GoJS</b> generates:
<a>DiagramEvent</a>s, <a>InputEvent</a>s, and <a>ChangedEvent</a>s.
This page talks about the latter, which are generated as <a>Diagram</a>s, <a>GraphObject</a>s,
<a>Model</a>s, or Model data objects are modified.
See the page <a href="events.html">Events</a> for the former two kinds of events.
</p>

<p>
<a>ChangedEvent</a>s in <b>GoJS</b> are notifications of state changes, mostly object property changes.
The ChangedEvent records the kind of change that occurred and enough information to be able to undo and redo them.
</p>
<p>
Changed events are produced by both <a>Model</a> and <a>Diagram</a>.
They are multicast events, so you can call <a>Model.addChangedListener</a> and <a>Diagram.addChangedListener</a>,
as well as the corresponding removeChangedListener methods.
For convenience you can also specify a Model change listener on a Diagram: <a>Diagram.addModelChangedListener</a>.
<a>ChangedEvent</a>s received by a Model change listener will have a non-null value for <a>ChangedEvent.model</a>.
Similarly, ChangedEvents received by a Diagram change listener will have non-null value for <a>ChangedEvent.diagram</a>.
</p>
<p>
A <a>Diagram</a> always registers itself as a listener on its <a>Model</a>, so that it can automatically
notice changes to the model and update its Parts accordingly.
Furthermore the <a>UndoManager</a>, if enabled, automatically listens to changes to both the model and the diagram,
so that it can record the change history and perform undo and redo.
</p>

<h2 id="ModelAndDataChanges">Model and Data changes</h2>

<h3 id="ModelPropertyChanges">Model property changes</h3>
<p>
Model ChangedEvents record state changes either to data in a model or to the <a>Model</a> itself.
ChangedEvents for models are generated by calls to <a>Model.setDataProperty</a> and by <a>Model</a> property setters.
</p>
<p>
For property changes, that information includes the <a>ChangedEvent.object</a> that was modified,
the <a>ChangedEvent.propertyName</a>, and the <a>ChangedEvent.oldValue</a> and <a>ChangedEvent.newValue</a> values
for that property.
Property changes are identified by the <a>ChangedEvent.change</a> property value being <a>ChangedEvent,Property</a>.
</p>
<p>
Some changes represent structural changes to the model, not just simple model data changes.
"Structural" changes are the insertion, modification, or removal of relationships that the model is responsible for maintaining.
In such cases the <a>ChangedEvent.modelChange</a> property will be a non-empty string naming the kind of change.
The following names for Property <a>ChangedEvent</a>s correspond to structural model data changes:
</p>
<ul>
  <li>"<b>nodeDataArray</b>", when the <a>Model.nodeDataArray</a> Array has been replaced</li>
  <li>"<b>nodeCategory</b>", due to a call to <a>Model.setCategoryForNodeData</a></li>

  <li>"<b>nodeGroupKey</b>", due to a call to <a>GraphLinksModel.setGroupKeyForNodeData</a></li>
  <li>"<b>linkDataArray</b>", when the <a>GraphLinksModel.linkDataArray</a> Array has been replaced</li>
  <li>"<b>linkFromKey</b>", due to a call to <a>GraphLinksModel.setFromKeyForLinkData</a></li>
  <li>"<b>linkToKey</b>", due to a call to <a>GraphLinksModel.setToKeyForLinkData</a></li>
  <li>"<b>linkFromPortId</b>", due to a call to <a>GraphLinksModel.setFromPortIdForLinkData</a></li>
  <li>"<b>linkToPortId</b>", due to a call to <a>GraphLinksModel.setToPortIdForLinkData</a></li>
  <li>"<b>linkLabelKeys</b>", due to a call to <a>GraphLinksModel.setLabelKeysForLinkData</a></li>
  <li>"<b>linkCategory</b>", due to a call to <a>GraphLinksModel.setCategoryForLinkData</a></li>

  <li>"<b>nodeParentKey</b>", due to a call to <a>TreeModel.setParentKeyForNodeData</a></li>
  <li>"<b>parentLinkCategory</b>", due to a call to <a>TreeModel.setParentLinkCategoryForNodeData</a></li>
</ul>
<p>
The value of <a>ChangedEvent.modelChange</a> will be one of these strings.
The value of <a>ChangedEvent.propertyName</a> depends on the name of the actual data property that was modified.
For example, for the model property change "linkFromKey", the actual property name defaults to "from".
But you might be using a different property name by having set <a>GraphLinksModel.linkFromKeyProperty</a> to some other data property name.
</p>
<p>
Any property can be changed on a node data or link data object, by calling <a>Model.setDataProperty</a>.
Such a call will result in the property name to be recorded as the <a>ChangedEvent.propertyName</a>.
These cases are treated as normal property changes, not structural model changes,
so <a>ChangedEvent.modelChange</a> will be the empty string.
The value of <a>ChangedEvent.object</a> will of course be the JavaScript object that was modified.
</p>
<p>
Some changes may happen temporarily because some code, such as in a Tool, might want to use temporary objects for their own purposes.
However your change listener might not be interested in such <a>ChangedEvent</a>s.
If that is the case, you may want to ignore the ChangedEvent if <a>Model.skipsUndoManager</a> (or <a>Diagram.skipsUndoManager</a>) is true.
</p>
<p>
Finally, there are property changes on the model itself.
For a listing of such properties, see the documentation for <a>Model</a>, <a>GraphLinksModel</a>, and <a>TreeModel</a>.
These cases are also treated as normal property changes, so <a>ChangedEvent.modelChange</a> will be the empty string.
Both <a>ChangedEvent.model</a> and <a>ChangedEvent.object</a> will be the model itself.
</p>

<h3 id="ModelCollectionChanges">Model collection changes</h3>
<p>
Other kinds of changed events include <a>ChangedEvent,Insert</a> and <a>ChangedEvent,Remove</a>.
In addition to all of the previously mentioned ChangedEvent properties used to record a property change,
the <a>ChangedEvent.oldParam</a> and <a>ChangedEvent.newParam</a> provide the "index" information
needed to be able to properly undo and redo the change.
</p>
<p>
The following names for Insert and Remove <a>ChangedEvent</a>s correspond to model changes to collections:
</p>
<ul>
  <li>"<b>nodeDataArray</b>", due to a call to <a>Model.addNodeData</a> or <a>Model.removeNodeData</a></li>
  <li>"<b>linkDataArray</b>", due to a call to <a>GraphLinksModel.addLinkData</a> or <a>GraphLinksModel.removeLinkData</a></li>
  <li>"<b>linkLabelKeys</b>", due to a call to <a>GraphLinksModel.addLabelKeyForLinkData</a>
      or <a>GraphLinksModel.removeLabelKeyForLinkData</a></li>
</ul>

<h3 id="Transactions">Transactions</h3>
<p>
The final kind of model changed event is <a>ChangedEvent,Transaction</a>.
These are not strictly object changes in the normal sense, but they do notify when a transaction starts or finishes,
or when an undo or redo starts or finishes.
</p>
<p>
The following values of <a>ChangedEvent.propertyName</a> describe the kind of transaction-related event that just occurred:
</p>
<ul>
  <li>"<b>StartingFirstTransaction</b>"</li>
  <li>"<b>StartedTransaction</b>"</li>
  <li>"<b>CommittingTransaction</b>"</li>
  <li>"<b>CommittedTransaction</b>"</li>
  <li>"<b>RolledBackTransaction</b>"</li>
  <li>"<b>StartingUndo</b>"</li>
  <li>"<b>FinishedUndo</b>"</li>
  <li>"<b>StartingRedo</b>"</li>
  <li>"<b>FinishedRedo</b>"</li>
</ul>
<p>
In each case the <a>ChangedEvent.object</a> is the <a>Transaction</a> holding a sequence of <a>ChangedEvent</a>s.
The <a>ChangedEvent.oldValue</a> is the name of the transaction --
the string passed to <a>UndoManager.startTransaction</a> or <a>UndoManager.commitTransaction</a>.
The various standard commands and tools that perform transactions document the transaction name(s) that they employ.
But your code can employ as many transaction names as you like.
</p>
<p class="box bg-danger">
As a general rule, you should not make any changes to the model or any of its data in a listener
for any Transaction ChangedEvent.
</p>

<h3 id="SavingModelWhenTransactionsComplete">Saving the Model when Transactions Complete</h3>
<p>
It is commonplace to want to update a server database when a transaction has finished.
Use the <a>ChangedEvent.isTransactionFinished</a> read-only property to detect that case.
You'll want to implement a Changed listener as follows:
</p>
<pre class="lang-js">
  // notice whenever a transaction or undo/redo has occurred
  diagram.addModelChangedListener(function(evt) {
    if (evt.isTransactionFinished) saveModel(evt.model);
  });
</pre>
<p>
The value of <a>Transaction.changes</a> will be a List of <a>ChangedEvent</a>s, in the order that they were recorded.
Those ChangedEvents represent changes both to the <a>Model</a> and to the <a>Diagram</a> or its <a>GraphObject</a>s.
Model changes will have <code>e.model !== null</code>; diagram changes will have <code>e.diagram !== null</code>.
</p>

<h3 id="IncrementallySavingChangesToModel">Incrementally Saving Changes to the Model</h3>
<p>
If you do not want to save the whole model at the end of each transaction, but only certain changes to the model,
you can iterate over the list of changes to pick out the ones that you care about.
For example, here is a listener that logs a message only when node data is added to or removed from the <a>Model.nodeDataArray</a>.
</p>
<pre class="lang-js">
  diagram.addModelChangedListener(function(evt) {
    // ignore unimportant Transaction events
    if (!evt.isTransactionFinished) return;
    var txn = evt.object;  // a Transaction
    if (txn === null) return;
    // iterate over all of the actual ChangedEvents of the Transaction
    txn.changes.each(function(e) {
      // ignore any kind of change other than adding/removing a node
      if (e.modelChange !== "nodeDataArray") return;
      // record node insertions and removals
      if (e.change === go.ChangedEvent.Insert) {
        console.log(evt.propertyName + " added node with key: " + e.newValue.key);
      } else if (e.change === go.ChangedEvent.Remove) {
        console.log(evt.propertyName + " removed node with key: " + e.oldValue.key);
      }
    });
  });
</pre>
<p>
The above listener will put out messages as the user adds nodes (including by copying) and deletes nodes.
The <a>ChangedEvent.propertyName</a> of the Transaction event (i.e. <i>evt</i> in the code above)
will be either "CommittedTransaction", "FinishedUndo", or "FinishedRedo".
Note that a "FinishedUndo" of the removal of a node is really adding the node,
just as the undo of the insertion of a node actually removes it.
</p>
<p>
Similarly, here is an example of noticing when links are connected, reconnected, or disconnected.
This not only checks for insertions to and removals from <a>GraphLinksModel.linkDataArray</a>,
but also changes to the "from" and the "to" properties of the link data.
</p>
<pre class="lang-js">
  diagram.addModelChangedListener(function(evt) {
    // ignore unimportant Transaction events
    if (!evt.isTransactionFinished) return;
    var txn = evt.object;  // a Transaction
    if (txn === null) return;
    // iterate over all of the actual ChangedEvents of the Transaction
    txn.changes.each(function(e) {
      // record node insertions and removals
      if (e.change === go.ChangedEvent.Property) {
        if (e.modelChange === "linkFromKey") {
          console.log(evt.propertyName + " changed From key of link: " +
                      e.object + " from: " + e.oldValue + " to: " + e.newValue);
        } else if (e.modelChange === "linkToKey") {
          console.log(evt.propertyName + " changed To key of link: " +
                      e.object + " from: " + e.oldValue + " to: " + e.newValue);
        }
      } else if (e.change === go.ChangedEvent.Insert && e.modelChange === "linkDataArray") {
        console.log(evt.propertyName + " added link: " + e.newValue);
      } else if (e.change === go.ChangedEvent.Remove && e.modelChange === "linkDataArray") {
        console.log(evt.propertyName + " removed link: " + e.oldValue);
      }
    });
  });
</pre>
<p>
Note: the above code only works for a <a>GraphLinksModel</a>, where the link data are separate JavaScript objects.
</p>

<p>
Look at the <a href="../samples/UpdateDemo.html">Update Demo</a> for a demonstration of how you can keep
track of changes to a model when a transaction is committed or when an undo or redo is finished.
The common pattern is to iterate over the ChangedEvents of the current Transaction
in order to decide what to record in a database.
</p>

<p>
It is also possible to send incremental updates to a database using <a>Model.toIncrementalJson</a> or <a>Model.toIncrementalData</a>,
which iterate over the changes in a transaction and group them into a JSON-formatted string or an object representing any updates.
</p>
<pre class="lang-js">
  diagram.addModelChangedListener(function(e) {
    // ignore unimportant Transaction events
    if (!evt.isTransactionFinished) return;
    var json = e.model.toIncrementalJson(e);
    var data = e.model.toIncrementalData(e);
    ... send to server/database ...
  });
</pre>


<h2 id="DiagramAndGraphObjectChanges">Diagram and GraphObject changes</h2>
<p>
Diagram ChangedEvents record state changes to <a>GraphObject</a>s or <a>RowColumnDefinition</a>s in a diagram,
or to a <a>Layer</a> in a diagram, or to the <a>Diagram</a> itself.
For such events, <a>ChangedEvent.diagram</a> will be non-null.
</p>
<p>
Most ChangedEvents for diagrams record property changes, such as when some code sets the <a>TextBlock.text</a> property
or the <a>Part.location</a> property.
There are a few places which generate ChangedEvents recording insertions into or removals from collections,
such as <a>Panel.insertAt</a>.
There are never any ChangedEvents for diagrams that are <a>ChangedEvent,Transaction</a>.
</p>
<p>
Although ChangedEvents for diagrams are important for undo/redo in order to retain visual fidelity,
one normally ignores them when saving models.  Only ChangedEvents for models record state changes to model data.
So for saving to a database, you will want to consider only those ChangedEvents for which <a>ChangedEvent.model</a> is non-null.
</p>

</div>
</div>
</body>
</html>
